package act

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"path"
	"strings"
	"text/template"

	"gitee.com/cxssang/goframe/lib/sql"

	"gitee.com/cxssang/genealogy/fs"
)

var SexMap = map[string]string{
	"男": "1",
	"女": "2",
}

var SexMap2 = map[string]string{
	"1": "男",
	"2": "女",
}

type Node struct {
	ID       int
	ParentID int
	Name     string
	Sex      string
	Spouse   string
	Birth    string
	Sort     int
	Children []*Node
}

var K = []byte{114, 113, 113, 102, 67, 50, 89, 82, 71, 99, 79, 66}

var HandleFunMap = map[string]http.HandlerFunc{}

func init() {
	HandleFunMap["/admin/"] = func(w http.ResponseWriter, r *http.Request) {
		t := template.New("container")
		tf, err := fs.FS.ReadFile("admin/container.html")
		if err != nil {
			DisplayError(w, err.Error())
			return
		}
		t, err = t.Parse(string(tf))
		if err != nil {
			DisplayError(w, err.Error())
			return
		}
		name, err := sql.Raw("select value from setting where key='name'").FetchOne()
		if name == nil {
			name = &sql.Row{}
			name.Set("value", "族谱")
		}
		if err != nil {
			DisplayError(w, err.Error())
			return
		}
		err = t.Execute(w, map[string]interface{}{
			"userId":   r.Header.Get("user-id"),
			"userName": r.Header.Get("user-name"),
			"name":     name.Get("value"),
		})
		if err != nil {
			log.Fatal(err)
		}
	}
}

func createInput(typ, name, label string, paramsJson ...string) string {
	params := map[string]interface{}{}
	if len(paramsJson) > 0 {
		err := json.Unmarshal([]byte(paramsJson[0]), &params)
		if err != nil {
			return err.Error()
		}
	}
	var width string
	var height string
	var required bool
	if val, ok := params["width"]; ok {
		width = val.(string)
	}
	if val, ok := params["height"]; ok {
		height = val.(string)
	}
	if val, ok := params["required"]; ok {
		required = val.(bool)
	}
	var styles []string
	if width != "" {
		styles = append(styles, fmt.Sprintf(`width="%s"`, width))
	}
	if height != "" {
		styles = append(styles, fmt.Sprintf(`height="%s"`, height))
	}

	strStr := strings.Builder{}
	if len(styles) > 0 {
		strStr.WriteString(` style="`)
		for i := 0; i < len(styles); i++ {
			strStr.WriteString(styles[i])
			strStr.WriteString(";")
		}
		strStr.WriteString(`"`)
	}
	if val, ok := params["class"]; ok {
		if val, ok := val.(string); ok {
			strStr.WriteString(` class="`)
			strStr.WriteString(val)
			strStr.WriteString(`"`)
		}
	}
	requiredStr := ""
	if required {
		strStr.WriteString(" required=true")
		requiredStr = " required=true"
	}
	var value string
	if val, ok := params["value"]; ok {
		value, ok = val.(string)
	}
	if value != "" {
		strStr.WriteString(` value="`)
		strStr.WriteString(value)
		strStr.WriteString(`"`)
	}
	s := fmt.Sprintf(`<div class="form-item"%s>`, requiredStr)
	switch typ {
	case "text", "password", "date", "number":
		return fmt.Sprintf(`%s<div class="label">%s</div><div class="input"><input type="%s" name="%s"%s/></div></div>`, s, label, typ, name, strStr.String())
	case "radio":
		var items []interface{}
		if val, ok := params["items"]; ok {
			items, _ = val.([]interface{})
		}
		checkedVal := ""
		if val, ok := params["checked"]; ok {
			checkedVal, _ = val.(string)
		}
		if items != nil {
			var radioGroup []string
			for _, item := range items {
				checked := ""
				itemMap, _ := item.(map[string]interface{})
				for n, v := range itemMap {
					if checkedVal == v {
						checked = " checked"
					}
					radioGroup = append(radioGroup, fmt.Sprintf(`<label><input name="%s" type="%s" value="%s"%s>%s</label>`, name, typ, v, checked, n))
				}
			}
			return fmt.Sprintf(`%s<div class="label">%s</div><div class="input radio-group">%s</div></div>`, s, label, strings.Join(radioGroup, ""))
		}
	case "textarea":
		return fmt.Sprintf(`%s<div class="label">%s</div><div class="input"><textarea name="%s"%s>%s</textarea></div></div>`, s, label, name, strStr.String(), value)
	}
	return ""
}

func searchBox(name, label, remoteUrlPath, defaultVal, defaultId string, required ...bool) string {
	var requiredStr string
	if len(required) > 0 {
		requiredStr = ` required="true"`
	}
	return fmt.Sprintf(`<div class="form-item search-box"%s>
                    <div class="label">%s</div>
                    <div class="input">
                        <input type="text" name="%s" data-val-id="%s" data-val="%s" value="%s" onkeydown="searchBoxOnInput(event,'%s')" onclick="_showSearchResultBox(event)">
                        <span class="iconfont icon-search" onclick="_searchBoxOnInput(event,'%s')"></span>
                    </div>
                </div>`, requiredStr, label, name, defaultId, defaultVal, defaultVal, remoteUrlPath, remoteUrlPath)
}

func CheckEmpty(w http.ResponseWriter, params ...string) bool {
	for i, v := range params {
		if v == "" {
			JsonError(w, params[i+1])
			return true
		}
	}
	return false
}

func Display(w http.ResponseWriter, r *http.Request, pageName string, data map[string]interface{}) {
	_, asAj := r.Header["Ajax"]
	if !asAj {
		http.NotFound(w, r)
		return
	}
	tf, _ := fs.FS.ReadFile(pageName + ".html")
	t := template.New(pageName)
	t.Funcs(template.FuncMap{
		"input":     createInput,
		"searchBox": searchBox,
		"add": func(a, b int) int {
			return a + b
		},
	})
	var err error
	t, err = t.Parse(string(tf))
	if err != nil {
		fmt.Println(err)
		http.NotFound(w, r)
		return
	}
	if data == nil {
		data = map[string]interface{}{}
	}
	data["aj"] = asAj
	err = t.Execute(w, data)
	if err != nil {
		fmt.Println(err)
		http.NotFound(w, r)
	}
}

func JsonSuccess(w http.ResponseWriter, data map[string]interface{}) {
	data = map[string]interface{}{
		"data":    data,
		"success": 1,
	}
	jdata, _ := json.Marshal(data)
	_, _ = w.Write(jdata)
}

func JsonError(w http.ResponseWriter, msg string) {
	data := map[string]interface{}{
		"msg":   msg,
		"error": 1,
	}
	jdata, _ := json.Marshal(data)
	_, _ = w.Write(jdata)
}

func DisplayError(w http.ResponseWriter, msg string, redirectUrl ...string) {
	tf, err := fs.FS.ReadFile("admin/error.html")
	t := template.New("error")
	t, err = t.Parse(string(tf))
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	data := map[string]string{
		"msg":         msg,
		"redirectUrl": "",
	}
	if len(redirectUrl) > 0 {
		data["redirectUrl"] = redirectUrl[0]
	}
	err = t.Execute(w, data)
	if err != nil {
		log.Println(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// 递归生成树函数
func BuildTree(nodes []*Node, parentID int) []*Node {
	res := make([]*Node, 0)
	for _, node := range nodes {
		if node.ParentID == parentID {
			node.Children = BuildTree(nodes, node.ID)
			res = append(res, node)
		}
	}
	return res
}

func CreateTree(nodes []*Node) string {
	var str = strings.Builder{}
	for _, node := range nodes {
		str.WriteString(`<div class="node">`)
		str.WriteString(fmt.Sprintf(`<span data-id="%d" data-name="%s" data-sex="%s" data-birth="%s" data-sort="%d">`, node.ID, node.Name, node.Sex, node.Birth, node.Sort))
		str.WriteString(node.Name)
		if node.Spouse != "" {
			str.WriteString(`<i>`)
			str.WriteString(node.Spouse)
			str.WriteString(`</i>`)
		}
		str.WriteString(`</span>`)
		if len(node.Children) > 0 {
			str.WriteString(`<div class="children">`)
			str.WriteString(CreateTree(node.Children))
			str.WriteString(`</div>`)
		}
		str.WriteString(`</div>`)
	}
	return str.String()
}

func DisplayM(w http.ResponseWriter, r *http.Request, pageName string, data map[string]interface{}) {
	name := fmt.Sprintf("%s.html", path.Base(pageName))
	t := template.New(name)
	t.Funcs(template.FuncMap{
		"active": func(url string) string {
			urls := strings.Split(url, "|")
			for _, url := range urls {
				if url == r.RequestURI {
					return ` class="active"`
				}
			}
			return ""
		},
	})
	t, err := t.ParseFS(fs.FS, "mobile/header.html", "mobile/footer.html", pageName+".html")
	if err != nil {
		log.Println(err)
		http.NotFound(w, r)
		return
	}
	if data == nil {
		data = map[string]interface{}{}
	}

	err = t.ExecuteTemplate(w, name, data)
	if err != nil {
		log.Println(err, "1")
		http.NotFound(w, r)
	}
}
